package com.swotxu.screwweb.config;

import cn.smallbun.screw.core.engine.EngineFileType;
import cn.smallbun.screw.core.engine.EngineTemplateType;
import cn.smallbun.screw.core.util.StringUtils;
import com.swotxu.screwweb.util.ScrewUtil;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.File;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Date: 2021/7/12 20:26
 * @Author: xu.hai
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "screw")
public class ScrewProperties implements Serializable {

    private static final long serialVersionUID = -5717282385221402953L;

    @XmlElementWrapper(name = "configurations")
    @XmlElement(name = "configuration")
    private List<ScrewDBInfo> configurations;

    @XmlAccessorType(XmlAccessType.FIELD)
    public static class ScrewDBInfo implements Serializable {
        private static final long serialVersionUID = 7569987006514813001L;
        /**
         * 唯一标识
         */
        @XmlAttribute(name = "id", required = true)
        private String id;
        //====================基础配置====================//
        /**
         * 标题
         */
        @XmlElement(name = "title")
        private String title;
        /**
         * 版本
         */
        @XmlElement(name = "version", defaultValue = "last")
        private String version;
        /**
         * 描述
         */
        @XmlElement(name = "description")
        private String description;

        //====================连接配置====================//
        /**
         * 用户名
         */
        @XmlElement(name = "username", required = true)
        private volatile String username;
        /**
         * 密码
         */
        @XmlElement(name = "password", required = true)
        private volatile String password;

        /**
         * 驱动类名称
         */
        @XmlElement(name = "driverClassName", required = true)
        private String driverClassName;
        /**
         * JDBC URL
         */
        @XmlElement(name = "jdbcUrl", required = true)
        private String jdbcUrl;

        //====================数据处理配置====================//
        /**
         * 忽略表名
         */
        @XmlElementWrapper(name = "ignoreTableNames")
        @XmlElement(name = "ignoreTableName")
        private List<String> ignoreTableNames;
        /**
         * 忽略表前缀
         */
        @XmlElementWrapper(name = "ignoreTablePrefixs")
        @XmlElement(name = "ignoreTablePrefix")
        private List<String> ignoreTablePrefixs;
        /**
         * 忽略表后缀
         */
        @XmlElementWrapper(name = "ignoreTableSuffixs")
        @XmlElement(name = "ignoreTableSuffix")
        private List<String> ignoreTableSuffixs;
        /**
         * 指定生成表名
         *
         * @see 1.0.3
         */
        @XmlElementWrapper(name = "designatedTableNames")
        @XmlElement(name = "designatedTableName")
        private List<String> designatedTableNames;
        /**
         * 指定生成表前缀
         *
         * @see 1.0.3
         */
        @XmlElementWrapper(name = "designatedTablePrefixs")
        @XmlElement(name = "designatedTablePrefix")
        private List<String> designatedTablePrefixs;
        /**
         * 指定生成表后缀
         *
         * @see 1.0.3
         */
        @XmlElementWrapper(name = "designatedTableSuffixs")
        @XmlElement(name = "designatedTableSuffix")
        private List<String> designatedTableSuffixs;

        //====================生成引擎配置====================//
        /**
         * 文件产生位置
         */
        @XmlElement(name = "fileOutputDir", defaultValue = "doc")
        private String fileOutputDir;
        /**
         * 生成实现
         */
        @XmlElement(name = "produceType", defaultValue = "freemarker")
        private EngineTemplateType produceType;
        /**
         * 文件名称
         */
        @XmlElement(name = "fileName")
        private String fileName;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getVersion() {
            return version;
        }

        public void setVersion(String version) {
            this.version = version;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getDriverClassName() {
            return driverClassName;
        }

        public void setDriverClassName(String driverClassName) {
            this.driverClassName = driverClassName;
        }

        public String getJdbcUrl() {
            return jdbcUrl;
        }

        public void setJdbcUrl(String jdbcUrl) {
            this.jdbcUrl = jdbcUrl;
        }

        public List<String> getIgnoreTableNames() {
            return ignoreTableNames;
        }

        public void setIgnoreTableNames(List<String> ignoreTableNames) {
            this.ignoreTableNames = ignoreTableNames;
        }

        public List<String> getIgnoreTablePrefixs() {
            return ignoreTablePrefixs;
        }

        public void setIgnoreTablePrefixs(List<String> ignoreTablePrefixs) {
            this.ignoreTablePrefixs = ignoreTablePrefixs;
        }

        public List<String> getIgnoreTableSuffixs() {
            return ignoreTableSuffixs;
        }

        public void setIgnoreTableSuffixs(List<String> ignoreTableSuffixs) {
            this.ignoreTableSuffixs = ignoreTableSuffixs;
        }

        public List<String> getDesignatedTableNames() {
            return designatedTableNames;
        }

        public void setDesignatedTableNames(List<String> designatedTableNames) {
            this.designatedTableNames = designatedTableNames;
        }

        public List<String> getDesignatedTablePrefixs() {
            return designatedTablePrefixs;
        }

        public void setDesignatedTablePrefixs(List<String> designatedTablePrefixs) {
            this.designatedTablePrefixs = designatedTablePrefixs;
        }

        public List<String> getDesignatedTableSuffixs() {
            return designatedTableSuffixs;
        }

        public void setDesignatedTableSuffixs(List<String> designatedTableSuffixs) {
            this.designatedTableSuffixs = designatedTableSuffixs;
        }

        public String getFileOutputDir() {
            return fileOutputDir;
        }

        public void setFileOutputDir(String fileOutputDir) {
            this.fileOutputDir = fileOutputDir;
        }

        public EngineTemplateType getProduceType() {
            return produceType;
        }

        public void setProduceType(EngineTemplateType produceType) {
            this.produceType = produceType;
        }

        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }

        @Override
        public String toString() {
            return "ScrewDBInfo{" +
                    "id='" + id + '\'' +
                    ", title='" + title + '\'' +
                    ", version='" + version + '\'' +
                    ", description='" + description + '\'' +
                    ", username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    ", driverClassName='" + driverClassName + '\'' +
                    ", jdbcUrl='" + jdbcUrl + '\'' +
                    ", ignoreTableNames=" + ignoreTableNames +
                    ", ignoreTablePrefixs=" + ignoreTablePrefixs +
                    ", ignoreTableSuffixs=" + ignoreTableSuffixs +
                    ", designatedTableNames=" + designatedTableNames +
                    ", designatedTablePrefixs=" + designatedTablePrefixs +
                    ", designatedTableSuffixs=" + designatedTableSuffixs +
                    ", fileOutputDir='" + fileOutputDir + '\'' +
                    ", produceType=" + produceType +
                    ", fileName='" + fileName + '\'' +
                    '}';
        }

        /**
         * 获取文件名（不含后缀）
         * @return 文件名
         */
        public String getDocName() {
            if (StringUtils.isNotBlank(this.getFileName())) {
                return this.getFileName();
            }
            return StringUtils.isBlank(this.getDescription())? this.getId() + "_" + this.getVersion()
                    : this.getId() + "_" + this.getDescription() + "_" + this.getVersion();
        }
        /**
         * 获取文件
         * @return 文件名
         */
        public File getDocPath(String fileType) {
            return getDocPath(ScrewUtil.getEngineFileType(fileType));
        }
        /**
         * 获取文件
         * @return 文件名
         */
        public File getDocPath(EngineFileType fileType) {
            File file;
            if (StringUtils.isBlank(this.getFileOutputDir())) {
                file = new File(System.getProperty("user.dir") + "/doc");
            } else {
                file = new File(this.getFileOutputDir());
            }
            if (!file.exists()) {
                file.mkdirs();
            }
            String suffix = fileType.getFileSuffix();
            file = new File(file, getDocName() + suffix);
            this.setFileOutputDir(file.getParent());
            return file;
        }
    }

    public List<ScrewDBInfo> getConfigurations() {
        return configurations;
    }

    public void setConfigurations(List<ScrewDBInfo> configurations) {
        this.configurations = configurations;
    }

    @Override
    public String toString() {
        return "ScrewDBConfig{" +
                "configurations=" + configurations +
                '}';
    }

    /**
     * 建立 dbInfo 的id映射
     * @return Map<id, dbInfo配置>
     * @throws IllegalStateException 若id不唯一，将抛出此异常
     */
    public Map<String, ScrewDBInfo> toIdMap() {
        return this.configurations.stream().collect(Collectors.toMap(ScrewDBInfo::getId, Function.identity()));
    }

    /**
     * 建立 dbInfo 的摘要映射
     * @return Map<文件唯一标识名, dbInfo配置>
     * @throws IllegalStateException 若最后生成的文件不唯一，将抛出此异常
     */
    public Map<String, ScrewProperties.ScrewDBInfo> toSignatureMap() {
        return this.configurations.stream().collect(
                Collectors.toMap(
                        info -> !org.springframework.util.StringUtils.isEmpty(info.getFileName()) ? info.getFileName()
                                : info.getId() + info.getDescription() + info.getVersion() + info.getTitle(),
                        Function.identity()
                ));
    }

    public Set<String> getIds() {
        return this.configurations.stream().map(ScrewProperties.ScrewDBInfo::getId).collect(Collectors.toSet());
    }
}
